package com.krtech.wecard.module.mayi.service.impl;

import cn.dev33.satoken.SaManager;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.crypto.digest.DigestAlgorithm;
import cn.hutool.crypto.digest.Digester;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.github.binarywang.wxpay.bean.notify.WxPayNotifyResponse;
import com.github.binarywang.wxpay.bean.notify.WxPayOrderNotifyResult;
import com.github.binarywang.wxpay.bean.order.WxPayMpOrderResult;
import com.github.binarywang.wxpay.bean.request.WxPayUnifiedOrderRequest;
import com.github.binarywang.wxpay.service.WxPayService;
import com.krtech.wecard.config.WecardRedisTemplate;
import com.krtech.wecard.framework.exception.BusinessException;
import com.krtech.wecard.framework.model.CommRes;
import com.krtech.wecard.framework.utils.LoginUtil;
import com.krtech.wecard.module.mayi.MayiPayEnums;
import com.krtech.wecard.module.mayi.entity.*;
import com.krtech.wecard.module.mayi.service.*;
import com.krtech.wecard.module.sys.entity.SysKeyValue;
import com.krtech.wecard.module.sys.entity.SysUser;
import com.krtech.wecard.module.sys.service.SysKeyValueService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import java.math.BigDecimal;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.krtech.wecard.module.mayi.mapper.MayiPayListMapper;
import org.springframework.transaction.annotation.Transactional;

@Slf4j
@Service
public class MayiPayListServiceImpl extends ServiceImpl<MayiPayListMapper, MayiPayList> implements MayiPayListService {

    @Value("${wx.pay.notifyUrl}")
    private String notifyUrl;

    @Autowired
    private WxPayService wxService;

    @Autowired
    private MayiCdkManageService cdkManageService;

    @Autowired
    private SysKeyValueService keyValueService;

    @Autowired
    private MayiMachineManageService machineManageService;

    @Autowired
    private MayiPayListMapper mayiPayListMapper;

    @Autowired
    private WecardRedisTemplate redisTemplate;

    @Autowired
    private MayiPayListMapper payListMapper;

    @Autowired
    private MayiDianzanLogService dianzanLogService;

    @Autowired
    private MayiCommentLogService commentLogService;



    @Autowired
    private MayiReviewMachineService reviewMachineService;

    @Autowired
    private MayiPaylistReplaceCdkService replaceCdkService;

    @Autowired
    private MayiVersionUpdateLogService versionUpdateLogService;

    @Transactional(rollbackFor = Exception.class)
    @Override
    public CommRes storePay(MayiPayEntity mayiPayEntity) {

        //获得系数
        SysKeyValue coefficientKeyValue = keyValueService.getOne(new QueryWrapper<SysKeyValue>()
                .eq(SysKeyValue.COL_SYS_KEY, MayiPayEnums.COEFFICIENT));

        if (coefficientKeyValue == null) {
            throw new BusinessException("比例系数不能为空，请联系管理员");
        }

        BigDecimal coefficientBig = new BigDecimal(coefficientKeyValue.getSysValue());

        //处理拉去支付数据
        String orderNumber = IdUtil.simpleUUID();
        mayiPayEntity.setOrderNumber(orderNumber);
        mayiPayEntity.setTradeType("NATIVE");

        //入库预存
        MayiPayList mayiPayList = new MayiPayList();
        mayiPayList.setCreatedTime(new Date());
        mayiPayList.setOrderNo(orderNumber);
        mayiPayList.setTotalfee(Long.valueOf(mayiPayEntity.getTotalFee()));
        mayiPayList.setUrl(mayiPayEntity.getUrl());
        mayiPayList.setGoalCount(mayiPayEntity.getGoalCount());

        BigDecimal realGoalCountBig = new BigDecimal(mayiPayEntity.getGoalCount()).multiply(coefficientBig);
        mayiPayList.setRealGoalCount(realGoalCountBig.intValue());

        mayiPayList.setStatus(MayiPayEnums.NOT_PAY);

        SysUser userInfo = LoginUtil.getUserInfo();
        mayiPayList.setFkUserCode(userInfo.getUserCode());
        this.save(mayiPayList);

        return this.generateOrder(mayiPayEntity);
    }

    @Override
    public CommRes generateOrder(MayiPayEntity payEntity) {
        try {
            // 1. 生成订单,上层传递
            //设置订单号


            // 2。 请求下单接口，创建订单，获得返回值
            WxPayUnifiedOrderRequest orderRequest = new WxPayUnifiedOrderRequest();
            //商品描述
            orderRequest.setBody("资助金额:" + payEntity.getTotalFee());


            orderRequest.setOutTradeNo(payEntity.getOrderNumber());
            //ip：在扫码枪使用，扫码枪必须是可信任的才可以使用

            orderRequest.setSpbillCreateIp("123.12.12.123");
//            orderRequest.setSpbillCreateIp(HttpServletRequestUtil.getRemoteIP2());
            //金额,单位是分
            orderRequest.setTotalFee(payEntity.getTotalFee());
            // 谁付钱
//            orderRequest.setOpenid(payEntity.getOpenid());

            orderRequest.setTradeType(payEntity.getTradeType());


            orderRequest.setNotifyUrl(notifyUrl);

            // 3. 生成带签名的信息
            WxPayMpOrderResult order = wxService.createOrder(orderRequest);

            return CommRes.ok(order);
        } catch (Exception e) {
            log.error("拉取预支付异常:" + e.getMessage());
            e.printStackTrace();
        }
        return CommRes.error("失败");
    }


    @Override
    public String notify(String xmlData) throws Exception {

        //todo更新状态

        //回调，当已经拿到的时候，能转成这个对象的时候，就是已经正常返回了
        WxPayOrderNotifyResult result = wxService.parseOrderNotifyResult(xmlData);
        //订单号
        String outTradeNo = result.getOutTradeNo();
        // 根据订单号去查询支付表，修改支付状态
        log.info("订单号:" + outTradeNo + "，回调");

        MayiPayList mayiPayList = this.getOne(new QueryWrapper<MayiPayList>().eq(MayiPayList.COL_ORDER_NO, outTradeNo));
        if (mayiPayList == null) {
            throw new BusinessException("回调异常，没有这个订单，请联系管理员");
        }

        //更新其已经完成支付的状态
        mayiPayList.setStatus(MayiPayEnums.PAY_AND_NOT_RUN);
        this.updateById(mayiPayList);

        //通知腾信支付，告知已经ok
        return WxPayNotifyResponse.success("成功");

    }


    @Override
    public boolean judgeVerificationCode(String phone, String verification) {

        //存入redis,并且限制过期时间，并且设置唯一键setNx，保证一个手机一分钟只能发送一次短信验证
        String currentVerification = SaManager.getSaTokenDao().get("getVerificationCode:" + phone);
        if (StrUtil.isEmpty(currentVerification)) {
            throw new BusinessException("当前验证码已过期，请重新输入");
        }

        if (!verification.equals(currentVerification)) {
            throw new BusinessException("您输入的验证码不正确，请重新输入");
        }

        return true;
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public boolean backAdd(MayiPayListVo addMailPay) {
        SysUser userInfo = LoginUtil.getUserInfo();

        if (userInfo == null) {
            throw new BusinessException("您没有登陆，请登陆！");
        }

        // 链接必须是ur开头
        if (!StrUtil.startWith(addMailPay.getUrl(),"https://ur.alipay.com") && !StrUtil.startWith(addMailPay.getUrl(),"https://render.alipay.com")){
            throw new RuntimeException("必须是以https://ur.alipay.com或者https://render.alipay.com开头的链接才能使用");
        }

        //判断cdk
        if (StrUtil.isEmpty(addMailPay.getCdkCode())) {
            throw new BusinessException("cdk不能为空");
        }

//        if (!MayiPayEnums.TYPE_YUEDU.equals(addMailPay.getType())){
//            throw new BusinessException("您没有选择阅读任务");
//        }

        //查询这个cdk余量
        MayiCdkManage cdkManage = cdkManageService.getOne(new QueryWrapper<MayiCdkManage>().eq(MayiCdkManage.COL_CDK_CODE, addMailPay.getCdkCode()));
        if (cdkManage == null) {
            throw new BusinessException("您输入的cdk异常，请联系管理员");
        }

//        if (!MayiPayEnums.TYPE_YUEDU.equals(cdkManage.getType())){
//            throw new BusinessException("您没有选择阅读任务");
//        }

        //判断结束时间
        if (DateUtil.compare(new Date(), cdkManage.getCdkEndTime()) > 0) {
            throw new BusinessException("您输入的cdk已过期，请联系管理员");
        }

        // 判断最低量
        SysKeyValue minAmountKey = keyValueService.getOne(new QueryWrapper<SysKeyValue>()
                .eq(SysKeyValue.COL_SYS_NAME, MayiPayEnums.MIN_AMOUNT));

        if (minAmountKey == null) {
            throw new BusinessException("最低刷单量未设置，请联系管理员");
        }
        int minAmount = Integer.parseInt(minAmountKey.getSysValue());
        if (addMailPay.getGoalCount() < minAmount) {
            throw new BusinessException("最低量为:" + minAmount + ",请重新设置");
        }

        //开始判断cdk量是否合理
        Long residueCount = cdkManage.getTotalUse() - cdkManage.getCurrentUse();
        if (addMailPay.getGoalCount() > residueCount) {
            throw new BusinessException("您cdk剩余量不足:" + addMailPay.getGoalCount() + "，请减少数量到:" + residueCount + "以下。或者联系管理员重新购买卡");
        }


        //获得系数
        //判断是ur还是render来获得系数
        SysKeyValue coefficientKeyValue = null;
        if (StrUtil.startWith(addMailPay.getUrl(),"https://ur.alipay.com")){
            coefficientKeyValue = keyValueService.getOne(new QueryWrapper<SysKeyValue>()
                    .eq(SysKeyValue.COL_SYS_KEY, MayiPayEnums.UR_COEFFICIENT));
        }else {
            coefficientKeyValue = keyValueService.getOne(new QueryWrapper<SysKeyValue>()
                    .eq(SysKeyValue.COL_SYS_KEY, MayiPayEnums.COEFFICIENT));
        }


        if (coefficientKeyValue == null) {
            throw new BusinessException("比例系数不能为空，请联系管理员");
        }

        BigDecimal coefficientBig = new BigDecimal(coefficientKeyValue.getSysValue());

        //减少cdk的使用数量
        cdkManage.setCurrentUse(cdkManage.getCurrentUse() + addMailPay.getGoalCount());

        //更新cdk数量
        cdkManageService.updateById(cdkManage);

        MayiPayList mayiPayList = new MayiPayList();
        mayiPayList.setCreatedTime(new Date());

        String orderNumber = IdUtil.simpleUUID();
        mayiPayList.setOrderNo(orderNumber);
        mayiPayList.setTotalfee(0L);
        mayiPayList.setUrl(addMailPay.getUrl());

        mayiPayList.setGoalCount(addMailPay.getGoalCount());
        BigDecimal realGoalCountBig = new BigDecimal(addMailPay.getGoalCount()).multiply(coefficientBig);
        mayiPayList.setRealGoalCount(realGoalCountBig.intValue());


        mayiPayList.setStatus(MayiPayEnums.PAY_AND_NOT_RUN);

        mayiPayList.setCdkCode(cdkManage.getCdkCode());
        mayiPayList.setFkUserCode(userInfo.getUserCode());

        //设置这次任务的系数
        mayiPayList.setCoefficient(coefficientBig.toString());

        mayiPayList.setType("1");


        return this.save(mayiPayList);
    }

    /**
     * 机器分组,根据机器码获得sort
     */
    @Override
    public Integer machineList(String machineId) {
//        ArrayList<List<MayiMachineManage>> resultList = new ArrayList<>();
        //考虑做成map，键是机器码，值是第几个分组
        Map<String, Integer> resultMap = new HashMap<>();

        SysKeyValue machineListCount = keyValueService.getOne(new QueryWrapper<SysKeyValue>()
                .eq(SysKeyValue.COL_SYS_KEY, MayiPayEnums.MACHINE_LIST_COUNT));

        Integer machineListCountSysValue = Integer.parseInt(machineListCount.getSysValue());

        //看有多少台机器
        List<MayiMachineManage> allMachineList = machineManageService
                .list(new QueryWrapper<MayiMachineManage>()
                        .eq(MayiMachineManage.COL_STATUS, 1));

        int machineCount = allMachineList.size();

        Integer pageCount = machineCount / machineListCountSysValue;


        for (int i = 1; i <= machineListCountSysValue; i++) {
            Page<MayiMachineManage> page = new Page<MayiMachineManage>();

            page.setCurrent(i);
            page.setSize(pageCount);

            Page<MayiMachineManage> machinePage = machineManageService.page(page);
            List<MayiMachineManage> machineList = machinePage.getRecords();


            allMachineList = allMachineList.stream().filter(allOne -> {
                return !machineList.stream().map(MayiMachineManage::getId).collect(Collectors.toList()).contains(allOne.getId());
            }).collect(Collectors.toList());

            if (i == machineListCountSysValue) {
                //说明是最后一次了，就把allMachineList添加进入machineList
                machineList.addAll(allMachineList);
            }

            //添加进一个分组集合
//            resultList.add(machineList);

            for (MayiMachineManage machineManage : machineList) {
                resultMap.put(machineManage.getMachineId(), i);

            }
        }
        return resultMap.get(machineId);
    }

    //获得热更新
    public MayiPayList getRunUpdate(String machineId){

        //查询当前是否有热更新任务
        List<MayiPayList> runUpdateList = this.list(new QueryWrapper<MayiPayList>()
                .in(MayiPayList.COL_STATUS, MayiPayEnums.PAY_AND_NOT_RUN, MayiPayEnums.PAY_AND_RUNING)
                .eq(MayiPayList.COL_TYPE, MayiPayEnums.TYPE_RUN_UPDATE));

        if (runUpdateList.size() > 0){
            //判断是否有iec链接
            //获得当前的版本
            SysKeyValue iecUrlObj = keyValueService.getOne(new QueryWrapper<SysKeyValue>()
                    .eq(SysKeyValue.COL_SYS_KEY, MayiPayEnums.IEC_UPDATE_VERSION_URL));
            String iec_url = iecUrlObj.getSysValue();
            if (StrUtil.isEmpty(iec_url)){
                return null;
            }


            MayiPayList runUpdate = runUpdateList.get(0);

            //获得当前的版本
            SysKeyValue IEC_UPDATE_VERSION = keyValueService.getOne(new QueryWrapper<SysKeyValue>()
                    .eq(SysKeyValue.COL_SYS_KEY, MayiPayEnums.IEC_UPDATE_VERSION));
            String update_versionSysValue = IEC_UPDATE_VERSION.getSysValue();
            //判断当前机器是否进行过这个版本的热更新
            List<MayiVersionUpdateLog> updateLogList = versionUpdateLogService.list(new QueryWrapper<MayiVersionUpdateLog>()
                    .eq(MayiVersionUpdateLog.COL_MACHINE_ID, machineId)
                    .eq(MayiVersionUpdateLog.COL_VERSION, update_versionSysValue));

            if (updateLogList.size() <= 0){
                return runUpdate;
            }
        }

        return null;
    }


    @Transactional(rollbackFor = Exception.class)
    @Override
    public MayiPayList getTaskByMachineId(String machineId) {
        // 最优先执行热更新
        MayiPayList runUpdate = getRunUpdate(machineId);
        if (runUpdate != null){
            return runUpdate;
        }


        //获得当前机器的分组
        Integer machineCount = machineList(machineId);

        //todo 判断机器码是否在评论机器码里面，如果在，则判断是否有评论任务，如果有评论任务，则直接返回评论任务
        MayiReviewMachine reviewMachine = reviewMachineService.getOne(new QueryWrapper<MayiReviewMachine>().eq(MayiReviewMachine.COL_MACHINE_ID, machineId));
        if (reviewMachine != null){
            //有这台评论机器
            List<MayiPayList> reviewList = this.list(new QueryWrapper<MayiPayList>()
                    .in(MayiPayList.COL_STATUS, MayiPayEnums.PAY_AND_NOT_RUN, MayiPayEnums.PAY_AND_RUNING)
                    .eq(MayiPayList.COL_TYPE, MayiPayEnums.TYPE_REVIEW)
                    .orderByAsc(MayiPayList.COL_ID));

            //这里进行类似点赞的方法
            MayiPayList commentTask = getCommentTask(reviewList, machineId);

            if (commentTask != null){
                return commentTask;
            }
        }

        //排除掉 这个机器已经完成了点赞的
        List<MayiDianzanLog> dianzanLogList = dianzanLogService.list(new QueryWrapper<MayiDianzanLog>()
                .eq(MayiDianzanLog.COL_MACHINE_ID, machineId));

        List<Integer> alreadyDianzanList = dianzanLogList.stream()
                .map(MayiDianzanLog::getFkPayListId)
                .collect(Collectors.toList());

        //排除掉，点赞已经满足了预期的 判断下面正在等待已经进行的点赞任务是否数量到达预期
        List<MayiPayList> dianzanTaskList = this.list(new QueryWrapper<MayiPayList>()
                .in(MayiPayList.COL_STATUS, MayiPayEnums.PAY_AND_NOT_RUN, MayiPayEnums.PAY_AND_RUNING)
                .eq(MayiPayList.COL_TYPE, 2));

        //获得所有的点赞任务计数
        if (dianzanTaskList.size() > 0){

            List<MayiPayList> satisfyDianzanList = dianzanTaskList.stream().filter(dianzanTask -> {
                Integer dianzanCount = dianzanTask.getReachCount();
                // 这儿有问题，咋么下面的satisfyDianzanList 都是0
                //没有点赞count
                if (dianzanCount == null){
                    dianzanCount = 0;
                }
                if (dianzanCount >= dianzanTask.getGoalCount()) {
                    //log.info("说明这个该被筛选掉，所以其id会被拿到");
                    //说明这个该被筛选掉，所以其id会被拿到
                    //为true是返回值
                    return true;
                } else {
                    return false;
                }
            }).collect(Collectors.toList());

            List<Integer> satisfyDianzanIdList = satisfyDianzanList.stream()
                    .map(MayiPayList::getId)
                    .collect(Collectors.toList());

            //得出最终被筛掉的结果
            if (satisfyDianzanIdList.size() > 0){
                alreadyDianzanList.addAll(satisfyDianzanIdList);
            }
        }



        //尝试获得自己的任务
        //判断当前任务数量 如果>= 3
        //本质上alreadyDianzanList这个是这台机器点赞过的所有任务，他没有点赞过这个任务
        List<MayiPayList> reayTask = null;
        if (alreadyDianzanList.size() > 0){
            reayTask = this.list(new QueryWrapper<MayiPayList>()
                    .in(MayiPayList.COL_STATUS, MayiPayEnums.PAY_AND_NOT_RUN, MayiPayEnums.PAY_AND_RUNING)
                    .notIn(MayiPayList.COL_ID,alreadyDianzanList)
                    .ne(MayiPayList.COL_TYPE, MayiPayEnums.TYPE_REVIEW)
                    .ne(MayiPayList.COL_TYPE, MayiPayEnums.TYPE_RUN_UPDATE)
                    .orderByAsc(MayiPayList.COL_ID));
        }else {
            reayTask = this.list(new QueryWrapper<MayiPayList>()
                    .in(MayiPayList.COL_STATUS, MayiPayEnums.PAY_AND_NOT_RUN, MayiPayEnums.PAY_AND_RUNING)
                    .ne(MayiPayList.COL_TYPE, MayiPayEnums.TYPE_REVIEW)
                    .ne(MayiPayList.COL_TYPE, MayiPayEnums.TYPE_RUN_UPDATE)
                    .orderByAsc(MayiPayList.COL_ID));
        }


        //这里的readyTask可能包含有type=2的 直接返回
        // 这个就是点赞任务
        List<MayiPayList> waitDianzanTask = reayTask.stream().filter(task -> task.getType().equals(MayiPayEnums.TYPE_DIANZAN))
                .collect(Collectors.toList());
        if (waitDianzanTask.size() > 0){
            MayiPayList canDianzanTask = dianzanCourse(waitDianzanTask, machineId);
            if (canDianzanTask != null){
                return canDianzanTask;
            }
        }

        //考虑把所有的点赞任务都给过滤掉,返回所有不是点赞的任务，这可能会出现新的问题
        reayTask = reayTask.stream()
                .filter(task-> !task.getType().equals(MayiPayEnums.TYPE_DIANZAN))
                .collect(Collectors.toList());

        //过滤掉再进行返回，保证最后的正确性
        if (reayTask.size() <= 0) {
            return null;
        }

        //可以增加sort的list
        List<MayiPayList> canChooseTask = null;

        if (reayTask.size() >= 3) {

            List<MayiPayList> oneReadyTask = reayTask.stream().filter(task -> {
                return (task.getSort() != null) && (task.getSort().equals(machineCount));
            }).collect(Collectors.toList());

            //如果有就直接返回
            if (oneReadyTask.size() > 0) {
                MayiPayList payList = oneReadyTask.get(0);

                return payList;
            }

            //把sort有值的给过滤了
            canChooseTask = reayTask.stream().filter(task -> {
                return task.getSort() == null;
            }).collect(Collectors.toList());

            if (canChooseTask.size() <= 0){

                MayiPayList payList = reayTask.get(0);

                return payList;
            }

            //没有，就开始获得第一行，并且把第一行上锁，然后
            //@Select("select * from cs_course_select_class where  id = #{classId} for update")
            MayiPayList lockMayiPay = canChooseTask.get(0);

//            mayiPayListMapper.lock(lockMayiPay.getId());
            //上锁,上锁失败的原因是5号序列出现了死锁
            Long setNx = redisTemplate.setNx("lockSort:" + machineCount, String.valueOf(machineCount));
            if (setNx <= 0) {
                //上锁失败
                log.error("setNx上锁失败");
                return null;
            }


            //更新这一条数据的sort
            lockMayiPay.setSort(machineCount);
            this.updateById(lockMayiPay);


            //解除
            redisTemplate.del("lockSort:" + machineCount);
            return lockMayiPay;
        }

        MayiPayList returnPayList = reayTask.get(0);
//        returnPayList.setSort(machineCount);
//        this.updateById(returnPayList);

        return returnPayList;

    }

    @Transactional(rollbackFor = Exception.class)
    public MayiPayList getCommentTask(List<MayiPayList> taskList,String machineId){
        //构造一个返回
        MayiPayList canDianzanTask = null;

        //这个本质上就是锁定一个点赞任务
        //保证这台机器不会被重复添加
        for (MayiPayList payList:taskList){

            //判断当前
            if (StrUtil.isNotEmpty(payList.getType()) &&payList.getType().equals(MayiPayEnums.TYPE_REVIEW)) {

                //从list中获得一个值，如果存在，并且在dianzanLog中不存在，就说明这个任务本来该执行，但是却没有执行
                boolean judgeSet = redisTemplate.judgeSet("comment:set:success:" + payList.getId(), machineId);
                if (judgeSet){
                    //在排在前面，就说明前面的抢成功了，然后检索log数据库中是否有这一条
                    MayiCommentLog commentLog = commentLogService.getOne(new QueryWrapper<MayiCommentLog>()
                            .eq(MayiCommentLog.COL_FK_PAY_LIST_ID, payList.getId())
                            .eq(MayiCommentLog.COL_MACHINE_ID, machineId));

                    if (commentLog == null){
                        //没有这一条，说明这个任务没有成功
                        return payList;
                    }
                }

                if (canDianzanTask != null){
                    return canDianzanTask;
                }

                //判断当前任务数量
                if (payList.getReachCount() >= payList.getGoalCount()){
                    //说明任务完成了，则对状态进行修改
                    //说明结束了
                    //判断是否有结束时间
                    if (payList.getEndTime() == null){
                        payList.setEndTime(new Date());
                    }
                    payList.setStatus(MayiPayEnums.PAY_AND_RUN_OVER);
                    this.updateById(payList);
                    continue;
                }

                //类型为点赞的时候
                Long decr = redisTemplate.decr("comment:" + payList.getId());

                if (decr >= 0){
                    //说明抢成功
                    //设置一个redis抢成功名单

                    //再去redis进行抢座
                    Long size = redisTemplate.luaSetAdd("comment:set:success:" + payList.getId(), machineId);

                    //再检查一下success数量
                    //对比现在锁定的数量和
                    if (size > payList.getRealGoalCount()){
                        return null;
                    }
                    canDianzanTask = payList;
                    if (canDianzanTask != null){
                        return canDianzanTask;
                    }
                }
//                else {
//                    //如果小于0，就判断是否数量达标，如果没有达标，就进行数量修复
//                    if (payList.getReachCount() < payList.getGoalCount()){
//                        Integer repairCount = payList.getGoalCount() - payList.getReachCount();
//                        redisTemplate.set("comment:"+urlHash,repairCount.toString());
//                    }
//                }
            }
        }
        return canDianzanTask;
    }

    /**
     * 计算的是有多少台机子开始点赞，只要额定的机器
     * @param payList
     */
    /**
     * 保证
     * @param payList
     */
    @Transactional(rollbackFor = Exception.class)
    public MayiPayList dianzanCourse(List<MayiPayList> taskList,String machineId){

        //构造一个返回
        MayiPayList canDianzanTask = null;



        //这个本质上就是锁定一个点赞任务
        //保证这台机器不会被重复添加
        for (MayiPayList payList:taskList){

            //计算一个hash
            String urlHash = urlHash(payList.getUrl());

            //问题在检索了等待的任务，没有检索库中的任务

            //从list中获得一个值，如果存在，并且在dianzanLog中不存在，就说明这个任务本来该执行，但是却没有执行
            boolean judgeSet = redisTemplate.judgeSet("dianzai:set:success:" + urlHash, machineId);
            //todo 问题在于 urlHash计算出来的值，在redis中不存在,加一个定时任务解决
//            boolean exists = redisTemplate.exists("dianzai:set:success:" + urlHash);

            if (judgeSet){
                //在排在前面，就说明前面的抢成功了，然后检索log数据库中是否有这一条
                MayiDianzanLog dianzanLog = dianzanLogService.getOne(new QueryWrapper<MayiDianzanLog>()
//                        .eq(MayiDianzanLog.COL_FK_PAY_LIST_ID, payList.getId())
                        .eq(MayiDianzanLog.COL_URL_HASH, urlHash)
                        .eq(MayiDianzanLog.COL_MACHINE_ID, machineId));

                if (dianzanLog == null){
                    //没有这一条，说明这个任务没有成功
                    return payList;
                }

                //优化，redis中有，数据库中也有，就说明这机器不该需要这个任务
                continue;
            }

            if (canDianzanTask != null){
                return canDianzanTask;
            }

            //判断当前任务数量
            if (payList.getReachCount() >= payList.getGoalCount()){
                //说明任务完成了，则对状态进行修改
                //说明结束了
                //判断是否有结束时间
                if (payList.getEndTime() == null){
                    payList.setEndTime(new Date());
                }
                payList.setStatus(MayiPayEnums.PAY_AND_RUN_OVER);
                this.updateById(payList);
                continue;
            }

            if (StrUtil.isNotEmpty(payList.getType()) &&payList.getType().equals(MayiPayEnums.TYPE_DIANZAN)){
                //类型为点赞的时候
                Long decr = redisTemplate.decr("dianzan:" + urlHash);

                if (decr >= 0){
                    //说明抢成功
                    //设置一个redis抢成功名单

                    Long size = redisTemplate.luaSetAdd("dianzai:set:success:" + urlHash, machineId);

                    //再检查一下success数量
                    //对比现在锁定的数量和
//                    if (size > payList.getRealGoalCount()){
//                        return null;
//                    }

                    //抢成功了，更新一下paylist里面的update时间
                    payList.setUpdatedTime(new Date());
                    this.updateById(payList);

                    canDianzanTask = payList;

                    //额外添加到一个list中，并以这个list为准，这个可以不用加，我在list中取前40即可
//                redisTemplate.setList("dianzai:carry:success:" + payList.getId(), machineId);
                }
//                else {
//                    //如果小于0，就判断是否数量达标，如果没有达标，就进行数量修复
//                    if (payList.getReachCount() < payList.getGoalCount()){
//                        Integer repairCount = payList.getGoalCount() - payList.getReachCount();
//                        redisTemplate.set("dianzan:"+urlHash,repairCount.toString());
//                    }
//                }



            }
        }
        return canDianzanTask;
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public boolean userStopTask(String orderNo) {


        //将状态改成3，并且返还剩余次数到别人的cdk卡里
        MayiPayList mayiPayList = this.getOne(new QueryWrapper<MayiPayList>()
                .eq(MayiPayList.COL_ORDER_NO, orderNo)
                .in(MayiPayList.COL_STATUS, MayiPayEnums.PAY_AND_NOT_RUN, MayiPayEnums.PAY_AND_RUNING));

        //判断任务状态，如果为1，2才可退
        if (mayiPayList == null) {
            throw new BusinessException("当前任务已经结束不可退");
        }

        mayiPayList.setStatus(MayiPayEnums.MYSELF_STOP);
        mayiPayList.setEndTime(new Date());

        //更新状态
        this.updateById(mayiPayList);

        //返还次数，计算比例
        BigDecimal realGoalCount = new BigDecimal(mayiPayList.getRealGoalCount());
        BigDecimal reachCount = new BigDecimal(mayiPayList.getReachCount());

        //比例
        BigDecimal bili = reachCount.divide(realGoalCount, 2, BigDecimal.ROUND_HALF_UP);

        if (bili.compareTo(new BigDecimal("1.00")) > 0) {
            bili = new BigDecimal("1.00");
        }

        BigDecimal goalCount = new BigDecimal(mayiPayList.getGoalCount());

        //该返还的数量
        BigDecimal resultReachCountBig = goalCount.multiply(bili);

        Integer resultReachCount = resultReachCountBig.intValue();

        //查询cdk情况
        MayiCdkManage cdk = cdkManageService.getOne(new QueryWrapper<MayiCdkManage>()
                .eq(MayiCdkManage.COL_CDK_CODE, mayiPayList.getCdkCode()));

        if (cdk != null && cdk.getCurrentUse() >= resultReachCount) {
            Integer returnCount = mayiPayList.getGoalCount() - resultReachCount;

            payListMapper.returnCdk(cdk.getCdkCode(), returnCount);
        }


        return true;

    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public boolean exception(String orderNumber, String machineId) {
        MayiPayList mayiPayList = this.getOne(new QueryWrapper<MayiPayList>().eq(MayiPayList.COL_ORDER_NO, orderNumber));
        if (mayiPayList == null) {
            throw new BusinessException("回调异常，没有这个订单，请联系管理员");
        }

        //判断是否有开始时间
        if (mayiPayList.getEndTime() == null) {
            mayiPayList.setEndTime(new Date());
        }


        //更新其已经完成支付的状态
        mayiPayList.setStatus(MayiPayEnums.PAY_AND_RUN_EXCEPTION);
        this.updateById(mayiPayList);

        // 恢复这个cdk的量
        MayiCdkManage cdkManage = cdkManageService.getOne(new QueryWrapper<MayiCdkManage>()
                .eq(MayiCdkManage.COL_CDK_CODE, mayiPayList.getCdkCode()));

        payListMapper.returnCdk(cdkManage.getCdkCode(), mayiPayList.getGoalCount());

        //todo 通知刷这个帖子的人
        //todo 如果是要公众号的话，我需要获得公众号的openId，太麻烦了

        return true;
    }


    //    @Transactional(rollbackFor = Exception.class)
    @Override
    public boolean pinnedTask(PinnedTaskVo pinnedTaskVo) {
        //置顶的对象
        MayiPayList pinnied = this.getById(pinnedTaskVo.getPinnedId());
        //要被替换的对象
        MayiPayList replace = this.getById(pinnedTaskVo.getReplaceId());

        //将pinnied的sort改成被替换着的sort
        pinnied.setSort(replace.getSort());
        this.updateById(pinnied);

        replace.setStatus(MayiPayEnums.PAY_AND_RUN_OVER);
        this.updateById(replace);

        //塞入redis，让task来扫描
        redisTemplate.set("replace:"+replace.getId(),pinnied.getId().toString(),1200);


        return true;
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public boolean dianzanOver(String orderNumber, String machineId) {
        MayiPayList mayiPayList = this.getOne(new QueryWrapper<MayiPayList>().eq(MayiPayList.COL_ORDER_NO, orderNumber));
        if (mayiPayList == null){
            throw new BusinessException("回调异常，没有这个订单，请联系管理员");
        }

        //判断是否已经结束了
        int judgeHasDianzan = dianzanLogService.count(new QueryWrapper<MayiDianzanLog>()
                .eq(MayiDianzanLog.COL_MACHINE_ID, machineId)
                .eq(MayiDianzanLog.COL_FK_PAY_LIST_ID, mayiPayList.getId()));
        if (judgeHasDianzan > 0){
            return true;
        }

        String urlHash = urlHash(mayiPayList.getUrl());

        //如果没有，则加入点赞日志
        MayiDianzanLog dianzanLog = new MayiDianzanLog();
        dianzanLog.setCreatedTime(new Date());
        dianzanLog.setMachineId(machineId);
        dianzanLog.setFkPayListId(mayiPayList.getId());
        dianzanLog.setUrlHash(urlHash);
        dianzanLogService.save(dianzanLog);


        //计算点赞日志的数量是否达到点赞数量，判断是否结束
        int countOver = dianzanLogService.count(new QueryWrapper<MayiDianzanLog>()
                .eq(MayiDianzanLog.COL_FK_PAY_LIST_ID, mayiPayList.getId()));

        //不使用自增，不准确，使用查询dianzanLog的数量，来增加
        mayiPayList.setReachCount(countOver);
        this.updateById(mayiPayList);


        if (countOver >= mayiPayList.getGoalCount()){
            //说明结束了
            //判断是否有结束时间
            if (mayiPayList.getEndTime() == null){
                mayiPayList.setEndTime(new Date());
            }
            mayiPayList.setStatus(MayiPayEnums.PAY_AND_RUN_OVER);
            return this.updateById(mayiPayList);
        }

        return true;
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public boolean backDianzanAdd(MayiPayListVo addMailPay) {
        SysUser userInfo = LoginUtil.getUserInfo();

        // 链接必须是ur开头
        if (!StrUtil.startWith(addMailPay.getUrl(),"https://ur.alipay.com")){
            throw new BusinessException("必须是以https://ur.alipay.com开头的链接才能使用");
        }

        if (userInfo == null) {
            throw new BusinessException("您没有登陆，请登陆！");
        }
        //判断cdk
        if (StrUtil.isEmpty(addMailPay.getCdkCode())) {
            throw new BusinessException("cdk不能为空");
        }

        SysKeyValue ratedDianzanSys = keyValueService.getOne(new QueryWrapper<SysKeyValue>()
                .eq(SysKeyValue.COL_SYS_NAME, MayiPayEnums.RATED_DIANZAN_COUNT));
        int ratedDianzan = Integer.parseInt(ratedDianzanSys.getSysValue());

        //判断当前链接的数量
        String urlHash = urlHash(addMailPay.getUrl());
        int currentUrlCount = dianzanLogService.count(new QueryWrapper<MayiDianzanLog>().eq(MayiDianzanLog.COL_URL_HASH, urlHash));

        //计算出这个还可以添加的数量
        int canUrlCount = ratedDianzan - currentUrlCount;
        if (addMailPay.getGoalCount() > canUrlCount){
            throw new BusinessException("单个链接最多点赞数量为:"+ratedDianzan+",您这次添加的任务超过了，您还可以为这个链接点赞:"+canUrlCount+"个");
        }

        //查询这个cdk余量
        MayiCdkManage cdkManage = cdkManageService.getOne(new QueryWrapper<MayiCdkManage>().eq(MayiCdkManage.COL_CDK_CODE, addMailPay.getCdkCode()));
        if (cdkManage == null) {
            throw new BusinessException("您输入的cdk异常，请联系管理员");
        }

        //判断结束时间
        if (DateUtil.compare(new Date(), cdkManage.getCdkEndTime()) > 0) {
            throw new BusinessException("您输入的cdk已过期，请联系管理员");
        }

        //判断cdk类型是否为点赞
        if (cdkManage.getType() != 2){
            throw new BusinessException("您的这张cdk不是点赞cdk");
        }

        if (!MayiPayEnums.TYPE_DIANZAN.equals(addMailPay.getType())){
            throw new BusinessException("您没有选择点赞任务");
        }

        // 判断最低量
        SysKeyValue minAmountKey = keyValueService.getOne(new QueryWrapper<SysKeyValue>()
                .eq(SysKeyValue.COL_SYS_NAME, MayiPayEnums.MIN_DIANZAN));

        if (minAmountKey == null) {
            throw new BusinessException("最低点赞量未设置，请联系管理员");
        }
        int minAmount = Integer.parseInt(minAmountKey.getSysValue());
        if (addMailPay.getGoalCount() < minAmount) {
            throw new BusinessException("最低点赞为量为:" + minAmount + ",请重新设置");
        }

        //开始判断cdk量是否合理
        Long residueCount = cdkManage.getTotalUse() - cdkManage.getCurrentUse();
        if (addMailPay.getGoalCount() > residueCount) {
            throw new BusinessException("您cdk剩余点赞量不足:" + addMailPay.getGoalCount() + "，请减少数量到:" + residueCount + "以下。或者联系管理员重新购买卡");
        }

        //减少cdk的使用数量
        cdkManage.setCurrentUse(cdkManage.getCurrentUse() + addMailPay.getGoalCount());

        cdkManageService.updateById(cdkManage);

        //点赞额外添加数量
        SysKeyValue dianzanAddKey = keyValueService.getOne(new QueryWrapper<SysKeyValue>()
                .eq(SysKeyValue.COL_SYS_NAME, MayiPayEnums.DIANZAN_ADD));

        if (dianzanAddKey == null) {
            throw new BusinessException("点赞额外数量未设置，请联系管理员");
        }

        int runningDianzan = this.count(new QueryWrapper<MayiPayList>()
                .eq(MayiPayList.COL_URL, addMailPay.getUrl()).eq(MayiPayList.COL_TYPE,MayiPayEnums.TYPE_DIANZAN)
                .in(MayiPayList.COL_STATUS, MayiPayEnums.PAY_AND_NOT_RUN, MayiPayEnums.PAY_AND_RUNING));

        if (runningDianzan > 0){
            throw new BusinessException("您上一个同一个链接还未结束，不能同时添加相同链接任务");
        }

        //点赞与评论比例
        int dianzanAdd = Integer.parseInt(dianzanAddKey.getSysValue());


        MayiPayList mayiPayList = new MayiPayList();
        mayiPayList.setCreatedTime(new Date());

        String orderNumber = IdUtil.simpleUUID();
        mayiPayList.setOrderNo(orderNumber);
        mayiPayList.setTotalfee(0L);
        mayiPayList.setUrl(addMailPay.getUrl());

        mayiPayList.setGoalCount(addMailPay.getGoalCount());
        mayiPayList.setCoefficient("1");
        mayiPayList.setReachCount(0);
        mayiPayList.setRealGoalCount(addMailPay.getGoalCount() + dianzanAdd);

        mayiPayList.setStatus(MayiPayEnums.PAY_AND_NOT_RUN);

        mayiPayList.setCdkCode(addMailPay.getCdkCode());
        mayiPayList.setFkUserCode(userInfo.getUserCode());

        //设置这次任务的系数
        mayiPayList.setType(addMailPay.getType());

        //计算序号
        this.save(mayiPayList);

        //判断redis中是否包含现在url的hash
        Integer redisCount = mayiPayList.getGoalCount() + dianzanAdd;
        redisTemplate.set("dianzan:"+urlHash,redisCount.toString());

        //判断是否需要增加评论，根据环境变量来
        SysKeyValue generateComment = keyValueService.getOne(new QueryWrapper<SysKeyValue>()
                .eq(SysKeyValue.COL_SYS_NAME, MayiPayEnums.GENERATE_COMMENT));

        if (generateComment.getSysValue().equals("1")){
            SysKeyValue commentAddKey = keyValueService.getOne(new QueryWrapper<SysKeyValue>()
                    .eq(SysKeyValue.COL_SYS_NAME, MayiPayEnums.COMMENT_ADD));

            if (commentAddKey == null) {
                throw new BusinessException("评论额外数量未设置，请联系管理员");
            }
            //点赞与评论比例
            int commentAdd = Integer.parseInt(commentAddKey.getSysValue());

            //根据点赞的数量/规格，来确定出多少条评论
            SysKeyValue dianzanToComment = keyValueService.getOne(new QueryWrapper<SysKeyValue>()
                    .eq(SysKeyValue.COL_SYS_NAME, MayiPayEnums.DIANZAN_TO_COMMENT));

            if (dianzanToComment == null) {
                throw new BusinessException("点赞与评论比例未设置，请联系管理员");
            }
            //点赞与评论比例
            int dianzanToCommentInt = Integer.parseInt(dianzanToComment.getSysValue());

            Integer dianzanGoalCount = addMailPay.getGoalCount();

            //计算出评论数量
            Integer commentCount = dianzanGoalCount / dianzanToCommentInt;

            MayiPayList comment = new MayiPayList();
            comment.setCreatedTime(new Date());

            String commentOrderNumber = IdUtil.simpleUUID();
            comment.setOrderNo(commentOrderNumber);
            comment.setTotalfee(0L);
            comment.setUrl(addMailPay.getUrl());

            comment.setGoalCount(commentCount);
            comment.setCoefficient("1");
            comment.setReachCount(0);
            comment.setRealGoalCount(commentCount + commentAdd);

            comment.setStatus(MayiPayEnums.PAY_AND_NOT_RUN);

            comment.setCdkCode("adminTest");
            comment.setFkUserCode(userInfo.getUserCode());

            //3:评论任务
            comment.setType(MayiPayEnums.TYPE_REVIEW);

            //计算序号
            this.save(comment);

            //判断redis中是否包含现在url的hash
            Integer commentCountRedis = commentCount + commentAdd;
            redisTemplate.set("comment:"+urlHash,commentCountRedis.toString());

        }

        return true;
    }

    /**
     * 让机器保活，在进行中和获得任务的时候调用
     * @param machineId
     * @return
     */
    @Transactional(rollbackFor = Exception.class)
    @Override
    public boolean keepAlive(String machineId,String orderNumber) {
        //判断是否有机器，没有就加，有就更新心跳
        //查找到机器，如果有，则直接修改，如果没有，则添加机器
        if (StrUtil.isEmpty(machineId)){
            return false;
        }

        MayiMachineManage machine = machineManageService.getOne(new QueryWrapper<MayiMachineManage>()
                .eq(MayiMachineManage.COL_MACHINE_ID, machineId));

        if (machine == null){
            MayiMachineManage newMachine = new MayiMachineManage();
            newMachine.setMachineId(machineId);
            newMachine.setCreatedTime(new Date());
            newMachine.setLastConnectTime(new Date());
            newMachine.setStatus(1);

            if (StrUtil.isNotEmpty(orderNumber)){
                newMachine.setFkOrderNo(orderNumber);
            }

            machineManageService.save(newMachine);
        }else {
            machine.setLastConnectTime(new Date());
            machine.setStatus(1);

            if (StrUtil.isNotEmpty(orderNumber)){
                machine.setFkOrderNo(orderNumber);
            }

            machineManageService.updateById(machine);
        }
        return true;
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public boolean backgroudDianzanAdd(MayiPayListVo addMailPay) {
        SysUser userInfo = LoginUtil.getUserInfo();

        // 链接必须是ur开头
        if (!StrUtil.startWith(addMailPay.getUrl(),"https://ur.alipay.com")){
            throw new BusinessException("必须是以https://ur.alipay.com开头的链接才能使用");
        }

        if (userInfo == null) {
            throw new BusinessException("您没有登陆，请登陆！");
        }


        if (!MayiPayEnums.TYPE_DIANZAN.equals(addMailPay.getType())){
            throw new BusinessException("您没有选择点赞任务");
        }

        SysKeyValue dianzanAddKey = keyValueService.getOne(new QueryWrapper<SysKeyValue>()
                .eq(SysKeyValue.COL_SYS_NAME, MayiPayEnums.DIANZAN_ADD));

        if (dianzanAddKey == null) {
            throw new BusinessException("点赞额外数量未设置，请联系管理员");
        }

        int runningDianzan = this.count(new QueryWrapper<MayiPayList>()
                .eq(MayiPayList.COL_URL, addMailPay.getUrl())
                .in(MayiPayList.COL_STATUS, MayiPayEnums.PAY_AND_NOT_RUN, MayiPayEnums.PAY_AND_RUNING));

        if (runningDianzan > 0){
            throw new BusinessException("您上一个同一个链接还未结束，不能同时添加相同链接任务");
        }

        //点赞与评论比例
        int dianzanAdd = Integer.parseInt(dianzanAddKey.getSysValue());


        MayiPayList mayiPayList = new MayiPayList();
        mayiPayList.setCreatedTime(new Date());

        String orderNumber = IdUtil.simpleUUID();
        mayiPayList.setOrderNo(orderNumber);
        mayiPayList.setTotalfee(0L);
        mayiPayList.setUrl(addMailPay.getUrl());

        mayiPayList.setGoalCount(addMailPay.getGoalCount());
        mayiPayList.setCoefficient("1");
        mayiPayList.setReachCount(0);
        mayiPayList.setRealGoalCount(addMailPay.getGoalCount() + dianzanAdd);

        mayiPayList.setStatus(MayiPayEnums.PAY_AND_NOT_RUN);

        mayiPayList.setCdkCode("adminTest");
        mayiPayList.setFkUserCode(userInfo.getUserCode());

        //设置这次任务的系数
        mayiPayList.setType(addMailPay.getType());

        //计算序号
        this.save(mayiPayList);

        //计算出当前的url的hash
        String urlHash = urlHash(mayiPayList.getUrl());

        //判断redis中是否包含现在url的hash
        Integer redisCount = mayiPayList.getGoalCount() + dianzanAdd;
        redisTemplate.set("dianzan:"+urlHash,redisCount.toString());


//        String hasDianZan = redisTemplate.get("dianzan:" + urlHash);

//        if (StrUtil.isEmpty(hasDianZan)){
//            //计算序号
//            Integer redisCount = mayiPayList.getGoalCount() + dianzanAdd;
//            redisTemplate.set("dianzan:"+urlHash,redisCount.toString());
//        }


        //判断是否需要增加评论，根据环境变量来
        SysKeyValue generateComment = keyValueService.getOne(new QueryWrapper<SysKeyValue>()
                .eq(SysKeyValue.COL_SYS_NAME, MayiPayEnums.GENERATE_COMMENT));

        if (generateComment.getSysValue().equals("1")){
            SysKeyValue commentAddKey = keyValueService.getOne(new QueryWrapper<SysKeyValue>()
                    .eq(SysKeyValue.COL_SYS_NAME, MayiPayEnums.COMMENT_ADD));

            if (commentAddKey == null) {
                throw new BusinessException("评论额外数量未设置，请联系管理员");
            }
            //点赞与评论比例
            int commentAdd = Integer.parseInt(commentAddKey.getSysValue());

            //根据点赞的数量/规格，来确定出多少条评论
            SysKeyValue dianzanToComment = keyValueService.getOne(new QueryWrapper<SysKeyValue>()
                    .eq(SysKeyValue.COL_SYS_NAME, MayiPayEnums.DIANZAN_TO_COMMENT));

            if (dianzanToComment == null) {
                throw new BusinessException("点赞与评论比例未设置，请联系管理员");
            }
            //点赞与评论比例
            int dianzanToCommentInt = Integer.parseInt(dianzanToComment.getSysValue());

            Integer dianzanGoalCount = addMailPay.getGoalCount();

            //计算出评论数量
            Integer commentCount = dianzanGoalCount / dianzanToCommentInt;

            MayiPayList comment = new MayiPayList();
            comment.setCreatedTime(new Date());

            String commentOrderNumber = IdUtil.simpleUUID();
            comment.setOrderNo(commentOrderNumber);
            comment.setTotalfee(0L);
            comment.setUrl(addMailPay.getUrl());

            comment.setGoalCount(commentCount);
            comment.setCoefficient("1");
            comment.setReachCount(0);
            comment.setRealGoalCount(commentCount + commentAdd);

            comment.setStatus(MayiPayEnums.PAY_AND_NOT_RUN);

            comment.setCdkCode("adminTest");
            comment.setFkUserCode(userInfo.getUserCode());

            //3:评论任务
            comment.setType(MayiPayEnums.TYPE_REVIEW);

            //计算序号
            this.save(comment);

            //判断redis中是否包含现在url的hash
            Integer commentCountRedis = commentCount + commentAdd;
//            redisTemplate.set("comment:"+urlHash,commentCountRedis.toString());
            redisTemplate.set("comment:"+comment.getId(),commentCountRedis.toString());

//            String hasComment = redisTemplate.get("comment:" + urlHash);
//            if (StrUtil.isEmpty(hasComment)){
//                //计算序号
//                Integer commentCountRedis = commentCount + commentAdd;
//                redisTemplate.set("comment:"+urlHash,commentCountRedis.toString());
//            }

//            Integer commentCountRedis = commentCount + commentAdd;
//            redisTemplate.set("comment:"+comment.getId(),commentCountRedis.toString());
        }

        return true;
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public boolean adminStopUr(String orderNo) {


        //将状态改成3
        MayiPayList mayiPayList = this.getOne(new QueryWrapper<MayiPayList>()
                .eq(MayiPayList.COL_ORDER_NO, orderNo)
                .in(MayiPayList.COL_STATUS, MayiPayEnums.PAY_AND_NOT_RUN, MayiPayEnums.PAY_AND_RUNING));

        //判断任务状态，如果为1，2才可退
        if (mayiPayList == null) {
            throw new BusinessException("当前任务已经结束不可退");
        }

        mayiPayList.setStatus(MayiPayEnums.PAY_AND_RUN_OVER);
        mayiPayList.setEndTime(new Date());
        mayiPayList.setReachCount(mayiPayList.getRealGoalCount());

        //手动停不退卡

        return this.updateById(mayiPayList);

    }


    @Transactional(rollbackFor = Exception.class)
    @Override
    public boolean editOrderCdkCode(MayiPayList mayiPayList) {
        if (StrUtil.isEmpty(mayiPayList.getOrderNo())){
            throw new BusinessException("必填参数订单号不能为空");
        }

        if (StrUtil.isEmpty(mayiPayList.getCdkCode())){
            throw new BusinessException("必填参数新cdkCode不能为空");
        }

        MayiPayList currentMayiPayList = this.getOne(new QueryWrapper<MayiPayList>().eq(MayiPayList.COL_ORDER_NO, mayiPayList.getOrderNo()));
        if (currentMayiPayList == null){
            throw new BusinessException("订单号错误，请联系管理员");
        }
        MayiPaylistReplaceCdk replaceCdk = new MayiPaylistReplaceCdk();
        replaceCdk.setCreatedTime(new Date());
        replaceCdk.setFkOrderNo(mayiPayList.getOrderNo());
        replaceCdk.setOldCdk(currentMayiPayList.getCdkCode());
        replaceCdk.setNewCdk(mayiPayList.getCdkCode());

        replaceCdkService.save(replaceCdk);

        currentMayiPayList.setCdkCode(mayiPayList.getCdkCode());

        return this.updateById(currentMayiPayList);
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public boolean commentEnd(String orderNumber, String machineId) {
        MayiPayList mayiPayList = this.getOne(new QueryWrapper<MayiPayList>().eq(MayiPayList.COL_ORDER_NO, orderNumber));
        if (mayiPayList == null){
            throw new BusinessException("回调异常，没有这个订单，请联系管理员");
        }
        //todo 问题就在于此，优化成只要回调，就增加

        //判断是否已经结束了
//        int judgeHasDianzan = commentLogService.count(new QueryWrapper<MayiCommentLog>()
//                .eq(MayiCommentLog.COL_MACHINE_ID, machineId)
//                .eq(MayiCommentLog.COL_FK_PAY_LIST_ID, mayiPayList.getId()));
//        if (judgeHasDianzan > 0){
//            return true;
//        }

        //如果没有，则加入点赞日志
        MayiCommentLog commentLog = new MayiCommentLog();
        commentLog.setCreatedTime(new Date());
        commentLog.setMachineId(machineId);
        commentLog.setFkPayListId(mayiPayList.getId());
        commentLogService.save(commentLog);


        //计算点赞日志的数量是否达到点赞数量，判断是否结束
        int countOver = commentLogService.count(new QueryWrapper<MayiCommentLog>()
                .eq(MayiCommentLog.COL_FK_PAY_LIST_ID, mayiPayList.getId()));

        //不使用自增，不准确，使用查询dianzanLog的数量，来增加
        mayiPayList.setReachCount(countOver);
        this.updateById(mayiPayList);


        if (countOver >= mayiPayList.getGoalCount()){
            //说明结束了
            //判断是否有结束时间
            if (mayiPayList.getEndTime() == null){
                mayiPayList.setEndTime(new Date());
            }
            mayiPayList.setStatus(MayiPayEnums.PAY_AND_RUN_OVER);
            return this.updateById(mayiPayList);
        }

        return true;
    }

    @Override
    public boolean backGroudCommentAdd(MayiPayListVo addMailPay) {
        SysUser userInfo = LoginUtil.getUserInfo();

        // 链接必须是ur开头
        if (!StrUtil.startWith(addMailPay.getUrl(),"https://ur.alipay.com")){
            throw new BusinessException("必须是以https://ur.alipay.com开头的链接才能使用");
        }

        if (userInfo == null) {
            throw new BusinessException("您没有登陆，请登陆！");
        }


        if (!MayiPayEnums.TYPE_REVIEW.equals(addMailPay.getType())){
            throw new BusinessException("您没有选择评论任务");
        }


        SysKeyValue commentAddKey = keyValueService.getOne(new QueryWrapper<SysKeyValue>()
                .eq(SysKeyValue.COL_SYS_NAME, MayiPayEnums.COMMENT_ADD));

        if (commentAddKey == null) {
            throw new BusinessException("评论额外数量未设置，请联系管理员");
        }
        //计算出当前的url的hash
        String urlHash = urlHash(addMailPay.getUrl());

        //点赞与评论比例
        int commentAdd = Integer.parseInt(commentAddKey.getSysValue());

        //计算出评论数量
        Integer commentCount = addMailPay.getGoalCount();

        MayiPayList comment = new MayiPayList();
        comment.setCreatedTime(new Date());

        String commentOrderNumber = IdUtil.simpleUUID();
        comment.setOrderNo(commentOrderNumber);
        comment.setTotalfee(0L);
        comment.setUrl(addMailPay.getUrl());

        comment.setGoalCount(commentCount);
        comment.setCoefficient("1");
        comment.setReachCount(0);
        comment.setRealGoalCount(commentCount + commentAdd);

        comment.setStatus(MayiPayEnums.PAY_AND_NOT_RUN);

        comment.setCdkCode("adminTest");
        comment.setFkUserCode(userInfo.getUserCode());

        //3:评论任务
        comment.setType(MayiPayEnums.TYPE_REVIEW);

        //计算序号
        this.save(comment);

        //判断redis中是否包含现在url的hash
        Integer commentCountRedis = commentCount + commentAdd;
//        redisTemplate.set("comment:"+urlHash,commentCountRedis.toString());
        redisTemplate.set("comment:"+comment.getId(),commentCountRedis.toString());

        return true;
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public boolean batchBackAdd(MayiPayListVo addMailPay) {
        SysUser userInfo = LoginUtil.getUserInfo();

        if (userInfo == null) {
            throw new BusinessException("您没有登陆，请登陆！");
        }



        //判断cdk
        if (StrUtil.isEmpty(addMailPay.getCdkCode())) {
            throw new BusinessException("cdk不能为空");
        }


        //查询这个cdk余量
        MayiCdkManage cdkManage = cdkManageService.getOne(new QueryWrapper<MayiCdkManage>().eq(MayiCdkManage.COL_CDK_CODE, addMailPay.getCdkCode()));
        if (cdkManage == null) {
            throw new BusinessException("您输入的cdk异常，请联系管理员");
        }



        //判断结束时间
        if (DateUtil.compare(new Date(), cdkManage.getCdkEndTime()) > 0) {
            throw new BusinessException("您输入的cdk已过期，请联系管理员");
        }

        // 判断最低量
        SysKeyValue minAmountKey = keyValueService.getOne(new QueryWrapper<SysKeyValue>()
                .eq(SysKeyValue.COL_SYS_NAME, MayiPayEnums.MIN_AMOUNT));

        if (minAmountKey == null) {
            throw new BusinessException("最低刷单量未设置，请联系管理员");
        }
        int minAmount = Integer.parseInt(minAmountKey.getSysValue());
        if (addMailPay.getGoalCount() < minAmount) {
            throw new BusinessException("最低量为:" + minAmount + ",请重新设置");
        }

        // 计算数量
        List<MayiPayList> urlVoList = addMailPay.getUrlVoList();

        if (urlVoList == null || urlVoList.size() <= 0){
            throw new BusinessException("批量url不能为空");
        }

        //计算本次批量任务的总数
        Integer currentTotalCount = 0;
        for (MayiPayList urlVo:urlVoList){
            Integer goalCount = urlVo.getGoalCount();
            if (goalCount == null || goalCount <= 0){
                currentTotalCount = currentTotalCount + addMailPay.getGoalCount();
            }else {
                currentTotalCount = currentTotalCount + urlVo.getGoalCount();
            }
        }

        //开始判断cdk量是否合理
        Long residueCount = cdkManage.getTotalUse() - cdkManage.getCurrentUse();
        if (currentTotalCount > residueCount) {
            throw new BusinessException("您cdk剩余量不足:" + addMailPay.getGoalCount() + "，请减少数量到:" + residueCount + "以下。或者联系管理员重新购买卡");
        }

        //开始批量生成保存参数
        List<MayiPayList> batchTask = urlVoList.stream().map(urlVo -> {
            if (!StrUtil.startWith(urlVo.getUrl(),"https://ur.alipay.com") && !StrUtil.startWith(urlVo.getUrl(),"https://render.alipay.com")){
                throw new RuntimeException("必须是以https://ur.alipay.com或者https://render.alipay.com开头的链接才能使用");
            }
            //获得系数
            //判断是ur还是render来获得系数
            SysKeyValue coefficientKeyValue = null;
            if (StrUtil.startWith(urlVo.getUrl(), "https://ur.alipay.com")) {
                coefficientKeyValue = keyValueService.getOne(new QueryWrapper<SysKeyValue>()
                        .eq(SysKeyValue.COL_SYS_KEY, MayiPayEnums.UR_COEFFICIENT));
            } else {
                coefficientKeyValue = keyValueService.getOne(new QueryWrapper<SysKeyValue>()
                        .eq(SysKeyValue.COL_SYS_KEY, MayiPayEnums.COEFFICIENT));
            }


            if (coefficientKeyValue == null) {
                throw new BusinessException("比例系数不能为空，请联系管理员");
            }

            BigDecimal coefficientBig = new BigDecimal(coefficientKeyValue.getSysValue());

            MayiPayList mayiPayList = new MayiPayList();
            mayiPayList.setCreatedTime(new Date());

            String orderNumber = IdUtil.simpleUUID();
            mayiPayList.setOrderNo(orderNumber);
            mayiPayList.setTotalfee(0L);
            mayiPayList.setUrl(urlVo.getUrl());

            Integer goalCount = urlVo.getGoalCount();
            if (goalCount == null || goalCount <= 0) {
                mayiPayList.setGoalCount(addMailPay.getGoalCount());
                BigDecimal realGoalCountBig = new BigDecimal(addMailPay.getGoalCount()).multiply(coefficientBig);
                mayiPayList.setRealGoalCount(realGoalCountBig.intValue());
            } else {
                mayiPayList.setGoalCount(urlVo.getGoalCount());
                BigDecimal realGoalCountBig = new BigDecimal(urlVo.getGoalCount()).multiply(coefficientBig);
                mayiPayList.setRealGoalCount(realGoalCountBig.intValue());
            }


            mayiPayList.setStatus(MayiPayEnums.PAY_AND_NOT_RUN);

            mayiPayList.setCdkCode(cdkManage.getCdkCode());
            mayiPayList.setFkUserCode(userInfo.getUserCode());

            //设置这次任务的系数
            mayiPayList.setCoefficient(coefficientBig.toString());

            mayiPayList.setType("1");
            return mayiPayList;
        }).collect(Collectors.toList());

        //减少cdk的使用数量
        cdkManage.setCurrentUse(cdkManage.getCurrentUse() + currentTotalCount);

        //更新cdk数量
        cdkManageService.updateById(cdkManage);




        return this.saveBatch(batchTask);
    }


    public static String urlHash(String url){
        Digester md5 = new Digester(DigestAlgorithm.MD5);
        String md5Hex = md5.digestHex(url);

        // 截取前32个字符作为结果
        // 注意：MD5本身就是32个十六进制字符，这里不需要截取，但为了演示如何操作
        String result = md5Hex.substring(0, 32); // 实际上md5Hex.length() == 32，所以这一步是多余的
        return result;
    }

}
















